/**
 * 
 */
package com.xpec.c4.db.dao;

import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;

import redis.clients.jedis.Jedis;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import com.ms.game.common.container.BasicSetting;
import com.ms.game.common.container.BasicSettingUtil;
import com.ms.game.common.container.CommissionRateInfo;
import com.ms.game.common.container.GameLimitInfo;
import com.ms.game.common.container.GameTypeInfo;
import com.ms.game.common.container.LevelInfoBinUtil;
import com.ms.game.db.dto.DataTransfer;
import com.ms.game.db.util.UpdateQueryBuilder;
import com.ms.game.invitefriend.InviteCodeProcessor;
import com.ms.game.invitefriend.bin.InviteCodeRewardBinUtil;
import com.ms.game.invitefriend.bin.InviteCodeType;
import com.ms.game.invitefriend.bin.UplineInviteRewardInfo;
import com.ms.game.invitefriend.db.DistributorInviteCodesModel;
import com.ms.game.invitefriend.db.InviteFriendDAO;
import com.ms.game.itembox.ChangeItem;
import com.ms.game.itembox.ChangeItemLister;
import com.ms.game.itembox.GameDropItemInfo;
import com.ms.game.itembox.GameDropItemParam;
import com.ms.game.itembox.ItemChangeParam;
import com.ms.game.itembox.ItemChangeWay;
import com.ms.game.itembox.db.ItemChangeProcessData;
import com.ms.game.itembox.db.ItemDAO;
import com.ms.game.level.LevelChangeWay;
import com.ms.game.level.db.LevelLogModel;
import com.ms.game.offlineSpin.OfflineSpinResultData;
import com.ms.game.rebate.RebateType;
import com.ms.game.rebate.db.RebateDAO;
import com.ms.player.Player;
import com.ms.player.PlayerAttributeType;
import com.ms.player.PlayerNickNameParser;
import com.ms.player.PlayerRedis;
import com.ms.player.UpdateAttribute;
import com.ms.player.UpdateAttributeList;
import com.ms.service.db.jedis.JedisManager;
import com.ms.service.error.ErrorCode;
import com.ms.service.module.account.AccountModule;
import com.ms.service.module.datacenterlite.GameKeepSeatData;
import com.xpec.c4.db.basic.BasicGameDAO;
import com.xpec.c4.db.hibernate.manager.HibernateManager;
import com.xpec.c4.db.model.PlayerKeepSeatModel;
import com.xpec.c4.db.model.PlayerModel;
import com.xpec.c4.db.model.basic.BasicGameModel;
import com.xpec.c4.game.common.Helper;
import com.xpec.c4.game.datamanager.agent.money.ChangeCoin;
import com.xpec.c4.game.datamanager.agent.money.ChangeCoinLister;
import com.xpec.c4.game.datamanager.agent.money.CoinType;
import com.xpec.c4.game.datamanager.agent.money.MoneyChangeParam;
import com.xpec.c4.game.time.TimeSys;
import com.xpec.c4.game.unit.UnitManager;
import com.xpec.c4.game.utility.GUID;
import com.xpec.c4.game.utility.GUIDFactory;
import com.xpec.c4.game.utility.GUIDType;
import com.xpec.c4.game.utility.GameUtility;
import com.xpec.c4.message.MessageTypes;
import com.xpec.c4.service.message.MessageAction;
import com.xpec.c4.service.module.BasicServiceModule;
import com.xpec.c4.service.module.ConsistentGroupName;
import com.xpec.c4.service.module.ModuleName;
import com.xpec.c4.service.module.ServerProperties;
import com.xpec.c4.service.module.common.ItemInfoManager;
import com.xpec.c4.service.module.common.PlayerCommonInfoManager;

/**
 * 玩家DAO
 * 
 * @author lipeilin
 * 
 */
public class PlayerDAO extends BasicGameDAO {
	
	public final static String PLAYER_LOGIN_TIME_TABLE_NAME_REDIS = "player_login_time";

	public final static String PLAYER_OFFLINE_SPIN_RESULT_TABLE_NAME_REDIS = "player_offline_spin_result";

	private static Log log = LogFactory.getLog(PlayerDAO.class);

	/**
	 * 通過玩家guid得到對應的賬戶的ID
	 * @param guid
	 * @return
	 * author: zhongzhengkai date: 2012 2012-8-27 下午07:44:49
	 */
	public static String loadPlayerAccountIDByGuid(GUID guid){
		Session session=HibernateManager.getSession();
		String accID=(String)session.createSQLQuery("select p.accountId from players p where p.guid=:guid").setParameter("guid", GUID.toString(guid)).uniqueResult();
		HibernateManager.endSession();
		return accID;
	}
	
	/**
	 * 保存實體對像
	 * @param entity 待保存的實體對像
	 */
	public static void save(PlayerModel entity){
		if(entity==null){
			return;
		}
		Session session=HibernateManager.getSession();
		session.save(entity);
	}
	
	/**
	 * 保存玩家信息
	 * 
	 * @param player
	 * @return
	 * 
	 */
	public static boolean savePlayer(Player player) {
		return HibernateManager.txCommitFailRollbackClearSession();
	}

	/**
	 * 保存玩家信息
	 * 
	 * @param player
	 * @param errorCode	失敗錯誤碼,參考MysqlErrorNumbers 
	 * @return
	 * 
	 */
	public static boolean savePlayer(Player player, int[] errorCode) {
		
		if(log.isInfoEnabled() && ServerProperties.isSaveConsoleLog(player.getUserType())){
			log.info("db savePlayer:" + player);	
		}
		
		PlayerModel model = DataTransfer.playerToModel(player, null);
		try{
			Session session = HibernateManager.txBegin();
			if(model.getLoginTime().getTime() == 0) {
				model.setLoginTime(model.getCreateTime());
			}
			session.save(model);
			return HibernateManager.txCommitFailRollbackClearSession(errorCode);
		}
		catch(HibernateException e){
			return HibernateManager.txOperationFailRollbackClearSession(e, errorCode);
		}
		
	}

	/**
	 * 保存多個玩家信息
	 * 
	 * @param players
	 * @return
	 * 
	 */
	public static boolean savePlayers(List<Player> players) {
		
		log.info("db savePlayers:" + players);
		
		List<PlayerModel> models = DataTransfer.transferPlayers(players);
		HibernateManager.txBegin();
		// long t = System.currentTimeMillis();
		for (PlayerModel playerModel : models) {
			if(playerModel.getLoginTime().getTime() == 0) {
				playerModel.setLoginTime(playerModel.getCreateTime());
			}
			save(playerModel);
		}
		boolean success = HibernateManager.txCommitFailRollbackClearSession();
		// if (success) {
		// log.info(" savePlayers costs " + (System.currentTimeMillis() - t));
		// }
		return success;
	}

	/**
	 * 保存多個玩家信息,按給定的一次批量存儲的玩家個數分多次把玩家列表中的所有玩家都存完,DB測試用
	 * 
	 * @param players
	 *            玩家列表
	 * @param saveNum
	 *            一次批量存儲的玩家個數
	 * @return author: zhongzhengkai date: 2011 2011-11-7 上午10:34:59
	 */
	public static long savePlayersOutTime(List<Player> players, int saveNum) {

		if (players.size() == 0) {
			return -1;
		}

		if (saveNum > players.size()) {
			saveNum = players.size();
		}

		List<PlayerModel> models = DataTransfer.transferPlayers(players);

		long t = System.currentTimeMillis();
		for (int i = 0; i < players.size(); i++) {
			if (i == 0 || i % saveNum == 0) {
				HibernateManager.txBegin();
			}
			PlayerModel playerModel = models.get(i);
			if(playerModel.getLoginTime().getTime() == 0) {
				playerModel.setLoginTime(playerModel.getCreateTime());
			}
			save(models.get(i));
			if ((i != 0 && (i + 1) % saveNum == 0) || i == players.size() - 1) {
				HibernateManager.txCommitFailRollbackClearSession();
			}
		}

		return System.currentTimeMillis() - t;
	}

	/**
	 * 保存多個玩家信息,DB測試用
	 * 
	 * @param players
	 * @return
	 * 
	 */
	public static long savePlayersOutTime(List<Player> players) {
		List<PlayerModel> models = DataTransfer.transferPlayers(players);
		HibernateManager.txBegin();
		long t = System.currentTimeMillis();
		for (PlayerModel playerModel : models) {
			if(playerModel.getLoginTime().getTime() == 0) {
				playerModel.setLoginTime(playerModel.getCreateTime());
			}
			save(playerModel);
		}
		boolean success = HibernateManager.txCommitFailRollbackClearSession();
		if (success) {
			return System.currentTimeMillis() - t;
		} else {
			return -1;
		}
	}

	/**
	 * 保存或更新玩家信息
	 * 
	 * @param player
	 * @return
	 * 
	 */
	public static boolean saveOrUpdatePlayer(Player player) {
		if (player == null) {
			if(log.isErrorEnabled()){
				log.error("saveOrUpdatePlayer player is null");
			}
			return false;
		}
		
		if(log.isInfoEnabled() && ServerProperties.isSaveConsoleLog(player.getUserType())){
			log.info("db saveOrUpdatePlayer:"+player);	
		}
		
		PlayerModel model = loadPlayerModelByGuid(player.getGuid());
		HibernateManager.txBegin();

		model = DataTransfer.playerToModel(player, model);

		saveOrUpdate(model);
		boolean flag = HibernateManager.txCommitFailRollbackClearSession();
		
		HelperDAO.deleteModels();
		return flag;
	}

	/**
	 * 保存或更新多個玩家信息
	 * 
	 * @param players
	 * @return
	 */
	public static boolean saveOrUpdatePlayers(List<Player> players) {
		
		log.info("db saveOrUpdatePlayers:"+players);
		
		if (players == null) {
			return false;
		}
		List<String> guids = new ArrayList<String>();
		for (Player player : players) {
			if (player != null) {
				guids.add(GUID.toString(player.getGuid()));
			}
		}
		List<PlayerModel> models = loadPlayerModelsByGuid(guids);
		HashMap<String, PlayerModel> modelMap = new HashMap<String, PlayerModel>();

		for (PlayerModel playerModel : models) {
			modelMap.put(playerModel.getGuid(), playerModel);
		}	
		
		HibernateManager.txBegin();
		// long t = System.currentTimeMillis();
		
		for(Player player: players)
		{
			if (player != null) {
				PlayerModel playerModel = modelMap.get(GUID.toString(player.getGuid()));
				playerModel = DataTransfer.transfer(player, playerModel);
				
				saveOrUpdate(playerModel);								
			}
		}
		
		boolean flag = HibernateManager.txCommitFailRollbackClearSession();					

		HelperDAO.deleteModels();
		return flag;
	}
	public static boolean saveOrUpdatePlayerModels(List<PlayerModel> players) {
		 if(players==null){
			 return false;
		 }
		HibernateManager.txBegin();
		for (PlayerModel player : players) {
			if(player == null) continue;
			saveOrUpdate(player);
		}
		boolean flag = HibernateManager.txCommitFailRollbackClearSession();
	//	HelperDAO.deleteModels();
		return flag;
	}
	
	public static void updatePlayerModelPasswordError(PlayerModel model){
		
		Session session = HibernateManager.txBegin();
		Query query = session
				.createQuery("update PlayerModel set errorTimes = :errorTimes, disableTime = :disableTime, "
						+ "errorTimesGift = :errorTimesGift, disableTimeGift = :disableTimeGift "
						+ "where guid = :guid");
		query.setParameter("guid", model.getGuid().toString());
		query.setParameter("errorTimes", model.getErrorTimes());
		query.setParameter("disableTime", model.getDisableTime());
		query.setParameter("errorTimesGift", model.getErrorTimesGift());
		query.setParameter("disableTimeGift", model.getDisableTimeGift());
		query.executeUpdate();
				
		HibernateManager.txCommitFailRollbackClearSession(); 
	}
	
	/**
	 * 更新玩家跨日相關資訊 (最後跨日登入時間、連續登入次數、包月stage) 
	 */
	public static void updatePlayerCrossDayInfo(Player player) {
		Session session = HibernateManager.txBegin();

		UpdateQueryBuilder builder = new UpdateQueryBuilder();
		builder.setSession(session);
		builder.setTableName("PlayerModel");
		builder.addSetValue("loginTime", GameUtility.longToDate(player.getLoginTime()));
		builder.addSetValue("continuousLoginCount", player.getContinuousLoginCount());
		builder.addSetValue("monthlyFeePayStage", player.getMonthlyFeePayStage());
		builder.addSetValue("storeRewardContinuousLoginCount", player.getStoreRewardContinuousLoginCount());
		builder.setWhereKey("guid", player.getGuid().toString());
		builder.executeQuery();

		HibernateManager.txCommitFailRollbackClearSession();
	}
	
	/**
	 * 更新玩家最後登入時間與IP
	 */
	public static void updatePlayerLastLoginInfo(Player player, Date currentTime, String playerIP, String platform) {
		Session session = HibernateManager.txBegin();
		UpdateQueryBuilder builder = new UpdateQueryBuilder();
		builder.setSession(session);
		builder.setTableName("PlayerModel");
		builder.addSetValue("lastloginTime", currentTime);
		builder.addSetValue("lastloginIP", playerIP);
		builder.addSetValue("lastLoginPlatform", platform);
		builder.setWhereKey("guid", player.getGuid().toString());
		builder.executeQuery();

		HibernateManager.txCommitFailRollbackClearSession();
	}
	
	public static void updatePlayerModelLogoutTime(GUID playerID, long logoutTime) {
		Session session = HibernateManager.txBegin();

		Date logoutDate = new Date(logoutTime);
		Query query = session
				.createQuery("update PlayerModel set logoutTime = :logoutTime where guid = :guid");
		query.setParameter("guid", playerID.toString());
		query.setParameter("logoutTime", logoutDate);
		query.executeUpdate();

		HibernateManager.txCommitFailRollbackClearSession();
	}
	
	public static HashMap<String, Integer> getPlayerLevelNameMapResult(List<String> guids){
		Session session = HibernateManager.getSession();
		Query query = session.createQuery("Select new map(guid as guid,level as level) From PlayerModel where guid in (:GUIDS)");
		query.setParameterList("GUIDS", guids);
		ArrayList<HashMap<String, Object>> result = (ArrayList<HashMap<String, Object>>) query.list();
		HibernateManager.endSession();
		
		HashMap<String, Integer> guidLevelMap = new HashMap<String, Integer>();
		for (HashMap<String, Object> r : result) {
			guidLevelMap.put((String)r.get("guid"), (Integer)r.get("level"));
		}
		return guidLevelMap;
	}
	
	
	
	
	/**
	 * 使用C幣增加經驗值
	 * @param currentModule
	 * @param param
	 * @param model
	 * @param updateAttrList
	 */
	public static void updatePlayerExpByGoldCoin(BasicServiceModule currentModule, MoneyChangeParam param, PlayerModel model, UpdateAttributeList updateAttrList) {
		
		// 指定的 game 是押注不列入升級計算
		GameTypeInfo gameInfo = BasicSettingUtil.getGameTypeInfo(param.getGameId());
		if (gameInfo.getIsBetLevelUp() == GameTypeInfo.FALSE) {
			return;
		}
		
		Player playerData = DataTransfer.transfer(model);
		//確認會員限制 是否可以升級
		int limitCode = GameLimitInfo.checkLimitByClassify(GameLimitInfo.BASIC_LV_UP, playerData.getVip(),playerData.getLevel(), playerData.isVerifiedMember(), playerData.isTrialAccount(), playerData.getUplineCode());
		if(limitCode != GameLimitInfo.TRUE){
			return;
		}		
		
		ChangeCoinLister coinLister = param.getChangeCoinLister();

		// 只有金幣押注才會算升級經驗值
		ChangeCoin coinInfo = coinLister.getCoinMap().get(CoinType.GOLD);
		if (coinInfo == null) {
			return;
		}
		
		GUID orderId = param.getOrderId();
		BigDecimal expChangeValue = coinInfo.getChangeValue();
		
		updatePlayerExp(currentModule, model, playerData, updateAttrList, orderId, expChangeValue);
		
	}
	
	/**
	 * 使用道具增加經驗值
	 * @param currentModule
	 * @param param
	 * @param model
	 * @param updateAttrList
	 */
	public static void updatePlayerExpByItem(BasicServiceModule currentModule, ItemChangeParam param, PlayerModel model,UpdateAttributeList updateAttrList) {
		Player playerData = DataTransfer.transfer(model);
		//確認會員限制 是否可以升級
		int limitCode = GameLimitInfo.checkLimitByClassify(GameLimitInfo.BASIC_LV_UP, playerData.getVip(),playerData.getLevel(), playerData.isVerifiedMember(), playerData.isTrialAccount(), playerData.getUplineCode());
		if(limitCode != GameLimitInfo.TRUE){
			return;
		}		
		
		GUID orderId = param.getOrderId();
		updatePlayerExp(currentModule, model, playerData, updateAttrList, orderId, param.getExpChangeValue());
	}
	
	/**
	 * 
	 * @param param
	 * @param model
	 * @param updateAttrList
	 * @param currentModule
	 *            用來寫入 log 的 module 實體
	 */
	public static void updatePlayerExp(BasicServiceModule currentModule, PlayerModel model, Player playerData, UpdateAttributeList updateAttrList, GUID orderId, BigDecimal expChangeValue) {

		BigDecimal beforeExp = model.getExp();
		int beforeLevel = model.getLevel();
		BigDecimal totalExp = Helper.add(beforeExp, expChangeValue);

		//取得當前累積經驗相關的參數
		HashMap<String, Long> expInfos = LevelInfoBinUtil.getExpInfos(totalExp);
		
		Long level = expInfos.get(PlayerLevelCalculator.LEVEL);
		model.setLevel(level.intValue());
		model.setExp(totalExp);
		
		if (updateAttrList != null) {
			Long currentLevelExp = expInfos.get(PlayerLevelCalculator.EXP_IN_CURRENT_LEVEL);
			Long toNextLevelExp = expInfos.get(PlayerLevelCalculator.EXP_TO_NEXT_LEVEL);
			
			updateAttrList.addUpdateValue(PlayerAttributeType.LEVEL, level);
			updateAttrList.addUpdateValue(PlayerAttributeType.EXP, totalExp);
			updateAttrList.addUpdateValue(PlayerAttributeType.EXP_IN_CURRENT_LEVEL, currentLevelExp);
			updateAttrList.addUpdateValue(PlayerAttributeType.EXP_TO_NEXT_LEVEL, toNextLevelExp);
		}

		if (updateAttrList == null) {
			Session session = HibernateManager.txBegin();
			String queryString = "update PlayerModel set level = :level, exp = :exp where guid = :guid";
			Query query = session.createQuery(queryString);
			query.setParameter("guid", model.getGuid());
			query.setParameter("level", model.getLevel());
			query.setParameter("exp", model.getExp());
			query.executeUpdate();

			HibernateManager.txCommitFailRollbackClearSession();
		}		
		
		LevelLogModel logModel = new LevelLogModel();
		logModel.setOrderId(orderId.toString());
		logModel.setAccountId(playerData.getAccountId().toString());
		logModel.setPlayerId(playerData.getGuid().toString());
		logModel.setPlayerName(playerData.getNameWithNumber());
		logModel.setExpBeforeChange(beforeExp);
		logModel.setExpChangeValue(expChangeValue);
		logModel.setExpAfterChange(totalExp);
		logModel.setLevelBefore(beforeLevel);
		logModel.setLevelAfter(model.getLevel());
		logModel.setChangeWay(LevelChangeWay.ADD_GET_EXP_FROM_GAME.getId());
		logModel.setChangeWayDesc(LevelChangeWay.ADD_GET_EXP_FROM_GAME.getDesc());
		currentModule.saveDbLog(logModel);
		
		playerData.setLevel(level.intValue());
		playerData.setExp(totalExp);
		
		
		//是否升級
		boolean isLevelUp = (level > beforeLevel) ? true : false; 
		
		if (isLevelUp) {
			onPlayerLevelUp(currentModule, model, playerData, orderId, beforeLevel);
		}
	}
	
	/**
	 * 角色升級
	 * @param currentModule
	 * @param model
	 * @param playerData
	 * @param orderId
	 * @param beforeLevel
	 */
	private static void onPlayerLevelUp(BasicServiceModule currentModule, PlayerModel model, Player playerData, GUID orderId, int beforeLevel) {
		// 檢查是否滿五十等 如果滿50等增加三組邀請碼到inviteCodeModel
		if (beforeLevel < 50 && playerData.getLevel() >= 50) {
			InviteCodeProcessor.addInviteCodeToPlayer(playerData.getGuid().toString());
		}
 
		// 檢查 有沒有uplineCode ， 有沒有邀請碼&&是否已經把上線的邀請獎勵加到回饋金
		if (GameUtility.isHaveString(model.getUplineId()) && GameUtility.isHaveString(model.getUplineCode())) {
			InviteCodeType inviteCodeType = InviteCodeProcessor.getInviteCodeType(model.getUplineCode());
			UplineInviteRewardInfo info = InviteCodeRewardBinUtil.getuplinInviteRewardInfo(inviteCodeType);
			if (inviteCodeType == InviteCodeType.DISTRIBUTOR_INVITE_CODE) {

				boolean isUplineReward = false, isLevelUpComm = false;
				// 還沒領過上線獎勵，並且符合資格
				if (!model.isAddUplineInviteReward() && info.isUseLevelCondition() && model.getLevel() >= info.getDownlineNeedLevel()) {
					isUplineReward = true;
				}

				
				int playerLevel = model.getLevel();
				CommissionRateInfo commRateInfo = LevelInfoBinUtil.getCommissionRateInfo(playerLevel, InviteCodeType.DISTRIBUTOR_INVITE_CODE);
				
				int commLevel = 0;
				// 不在可以抽傭範圍
				if (commRateInfo != null) {
					commLevel = commRateInfo.getLvBeg();
					isLevelUpComm = true;
				}

			} else if(!model.isAddUplineInviteReward()){ //一般邀請碼，上線獎勵，沒領過的
				sendUplineInviteCodeReward(model, info, beforeLevel);
			}
		}
	}
	
	/**
	 * 將給過上線獎勵的玩家改成true
	 * @param guid
	 */
	public static void markPlayerAddUplineReward(String guid){
		Session session = HibernateManager.txBegin();
		Query query = session.createQuery("update PlayerModel set addUplineInviteReward = true where guid = :guid");
		query.setParameter("guid", guid);
		query.executeUpdate();
		HibernateManager.txCommitFailRollbackClearSession();
	}
	
	/**
	 * 一般遊戲 給上線獎勵
	 */
	private static void sendUplineInviteCodeReward(PlayerModel model,UplineInviteRewardInfo info, int beforeLevel){	
		int needLevel = info.getDownlineNeedLevel();
		// 上線獎勵檢查等級 之前不能領，現在可以領
		if (info.isUseLevelCondition() && beforeLevel < needLevel && model.getLevel() >= info.getDownlineNeedLevel()) {
			// 給上線獎勵
			RebateDAO.addUplineInviteCodeReward(model, model.getUplineId(), (int) info.getUplineReward(), RebateType.UPLINE_INVITE_AWARD);

			// 標記給過上線獎勵
			markPlayerAddUplineReward(model.getGuid());

			// 送紅點
			sendUplineRewardRedPoint(GUID.parseUUID(model.getUplineId()));
		}
	}
	
	/**
	 * 送上線獎勵的紅點
	 * @param guid
	 */
	private static void sendUplineRewardRedPoint(GUID guid){
		// 看上線是不是在線上
		GUID sessionId = PlayerCommonInfoManager.getInstance().getSessionByPlayerGuid(guid);
		if (sessionId != null) {
			// 是的話 請client改時間
			MessageAction.sendMessage(sessionId, MessageTypes.S_REBATE_MODIFY_TIME, "");
		}
	}
	
	
	/**
	 * 經銷商邀請碼給上線獎勵，並且送紅點
	 * @param model
	 * @param inviteCodesModel
	 * @param reward
	 */
	private static void addUplineRewardSendRedPoint(byte toType ,PlayerModel model, String uplineId, int designatedLevel, DistributorInviteCodesModel inviteCodesModel, int reward) {
		
		//給獎勵
		RebateDAO.addUplineInviteCodeReward(model, uplineId, reward, RebateType.UPLINE_INVITE_AWARD_DISTRIBUTOR);
		
		
		InviteFriendDAO.insertDistributorInviteCodeDesignatedLevelCom(toType, model, inviteCodesModel, reward);
		
		InviteFriendDAO.insetDistributorInviteCodeDesignatedLevelComSum(toType ,model, inviteCodesModel, reward);
			
		//送紅點
		sendUplineRewardRedPoint(GUID.parseUUID(uplineId));
	}
	
	
	
	
	/**
	 * 透過屬性更新清單寫入更新項目到 db
	 * <p>
	 * 更新清單中有什麼就寫什麼到 db
	 * 
	 * @param playerGuid
	 *            player guid
	 * @param updatedAttrList
	 *            屬性更新清單
	 * @return true: 成功<br>
	 *         false: 失敗
	 */
	public static boolean updatePlayerFromAttrList(String playerGuid, ArrayList<UpdateAttribute> updatedAttrList, int userType) {
		if (updatedAttrList == null) {
			return false;
		}

		if (updatedAttrList.size() == 0) {
			return false;
		}

		Session session = HibernateManager.txBegin();

		UpdateQueryBuilder builder = new UpdateQueryBuilder();
		builder.setSession(session);
		builder.setTableName("PlayerModel");

		for (UpdateAttribute attr : updatedAttrList) {
			switch (attr.getId()) {
			case PlayerAttributeType.GOLD_COIN:
				builder.addSetValue("goldCoin", attr.getValueBigDecimal());
				break;

			case PlayerAttributeType.TRIAL_COIN:
				builder.addSetValue("trialCoin", attr.getValueBigDecimal());
				break;

			case PlayerAttributeType.LUCKY_COIN:
				builder.addSetValue("luckyCoin", attr.getValueBigDecimal());
				break;

			case PlayerAttributeType.LEVEL:
				builder.addSetValue("level", attr.getValueInt());
				break;

			case PlayerAttributeType.VIP:
				builder.addSetValue("vip", attr.getValueInt());
				break;

			case PlayerAttributeType.SILENT_EXPIRE_TIME:
				builder.addSetValue("silentExpireTime", attr.getValueLong());
				break;

			case PlayerAttributeType.NAME:
				builder.addSetValue("name", attr.getValue());
				break;

			case PlayerAttributeType.EXP:
				builder.addSetValue("exp", attr.getValueBigDecimal());
				break;

			case PlayerAttributeType.BANK_GOLD_COIN:
				builder.addSetValue("bankGoldCoin", attr.getValueBigDecimal());
				break;

			case PlayerAttributeType.MONTHLY_FEE_END_TIME:
				builder.addSetValue("monthlyFeeEndTime", GameUtility.longToDate(attr.getValueLong()));
				break;

			case PlayerAttributeType.LAST_GET_LUCKY_COIN_TIME:
				builder.addSetValue("lastGetLuckyCoinTime", attr.getValueLong());
				break;
				
			case PlayerAttributeType.MONTHLY_AWARD_TIME:
				builder.addSetValue("monthlyAwardTime", GameUtility.longToDate(attr.getValueLong()));
				break;
				
			case PlayerAttributeType.VERIFY_REWARD_COUNT:
				builder.addSetValue("firstCreateReward", attr.getValueInt());
				break;
				
			case PlayerAttributeType.PURCHASE_REWARD_COUNT:
				builder.addSetValue("firstStoreReward", attr.getValueInt());
				break;
				
			case PlayerAttributeType.OFFLINE_PLAY_RESULT:
				builder.addSetValue("offlinePlayResult", attr.getValue());
				break;

			case PlayerAttributeType.VIP_EXP:
				builder.addSetValue("vipExp", attr.getValueLong());
				break;

			case PlayerAttributeType.STORE_REWARD_CONTINUOUS_LOGIN_COUNT:
				builder.addSetValue("storeRewardContinuousLoginCount", attr.getValueInt());
				break;	
				
			case PlayerAttributeType.INVITE_CODE:
				builder.addSetValue("inviteCode", attr.getValue());
				break;

			case PlayerAttributeType.CRYSTAL:
				builder.addSetValue("crystal", attr.getValueInt());
				break;

			case PlayerAttributeType.NAME_MODIFY_TIME:
				builder.addSetValue("nameModifyTime", GameUtility.longToDate(attr.getValueLong()));
				break;

			case PlayerAttributeType.NAME_NUMBER:
				builder.addSetValue("nameNumber", attr.getValue());
				break;
				
			case PlayerAttributeType.EXP_IN_CURRENT_LEVEL:
				break;
			case PlayerAttributeType.EXP_TO_NEXT_LEVEL:
				break;

			case PlayerAttributeType.TMP_CASH:
				builder.addSetValue("tmpCash", attr.getValueBigDecimal());
				break;

			default:
				if (log.isErrorEnabled()) {
					log.error("PlayerDAO.updatePlayerFromAttrList() unknow attribute type " + attr.getId(),
							new RuntimeException());
				}
				break;
			} // switch (attr.getId()) {
		} // for (UpdateAttribute attr : updatedAttrList) {

		builder.setWhereKey("guid", playerGuid);

		try {
			builder.executeQuery();
		} catch (Exception e) {
			if (log.isErrorEnabled()) {
				log.error("PlayerDAO.updatePlayerFromAttrList() fail, " + e);
			}

			HibernateManager.txCommitFailRollbackClearSession();
			return false;
		}

		boolean res = HibernateManager.txCommitFailRollbackClearSession();

		if (log.isInfoEnabled() && ServerProperties.isSaveConsoleLog(userType)) {
			log.info("update player " + playerGuid + ": " + updatedAttrList.toString() + " res=" + res);
		}

		return res;
	}

	/**
	 * 通過GUID讀取邏輯上未刪除的玩家VO，先在活躍表裡查，沒查到的話再到非活躍表裡查<br>
	 * 如果非活躍表有，則移動至非活躍表，並返回<br>
	 * 
	 * @param guid
	 * @return
	 * @throws InactiveToActiveException
	 * 
	 */
	public static Player loadPlayerByGuid(GUID guid) {
		if (guid == null) {
			return null;
		}

		// String hexID = guid.toString();
		PlayerModel model = loadPlayerModelByGuid(guid);
		if (model != null) {
			Player player = DataTransfer.transfer(model);
			
			if(log.isInfoEnabled() && ServerProperties.isSaveConsoleLog(player.getUserType())){
				log.info("db loadPlayerByGuid:" + player);	
			}

			return player;
		}
		
		return null;
	}

	/**
	 * 通過GUID列表獲得邏輯上未刪除的玩家VO，列表先在活躍表裡查，沒查到的話再到非活躍表裡查<br>
	 * 如果非活躍表有，則移動至非活躍表，並返回<br>
	 * 
	 * @param guids
	 * @return
	 * @throws InactiveToActiveException
	 */
	public static List<Player> loadPlayersByGuids(List<String> guids) {
		if (guids == null || guids.size() == 0) {
			return null;
		}

		List<PlayerModel> models = loadPlayerModelsByGuid(guids);
		HibernateManager.endSession();
		return DataTransfer.transferPlayerModels(models);
	}

	/**
	 * 活躍表裡讀取邏輯上未刪除的玩家PO
	 * 
	 * @param guid
	 * @return
	 * 
	 */
	public static PlayerModel loadPlayerModelByGuid(GUID guid) {
		if (guid == null) {
			return null;
		}
		Session session = HibernateManager.getSession();
		Criteria criteria = session.createCriteria(PlayerModel.class);
		criteria.add(Restrictions.eq("guid", GUID.toString(guid)));
		// add(estrictions.eq("deletedFlag", false));
		List list = criteria.list();
		HibernateManager.endSession();

		if (list != null && list.size() > 0) {
			return (PlayerModel) list.get(0);
		} else {
			return null;
		}
	}

	/**
	 * 用邀請碼載入玩家資料
	 * 
	 * @param inviteCode
	 *            邀請碼
	 * @return player model
	 */
	public static PlayerModel loadPlayerModelByInviteCode(String inviteCode) {
		if (inviteCode == null) {
			return null;
		}
		Session session = HibernateManager.getSession();
		Query query = session.createQuery("from PlayerModel Where guid = (select playerId from InviteCodeModel where inviteCode = :inviteCode) ");
		query.setParameter("inviteCode", inviteCode);
		PlayerModel model = (PlayerModel) query.uniqueResult();
		HibernateManager.endSession();
		return model;
	}

	/**
	 * 通過GUID列表讀取多個活躍玩家PO
	 * 
	 * @param guids
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static List<PlayerModel> loadPlayerModelsByGuid(List<String> guids) {
		if (guids == null || guids.size()==0) {
			return null;
		}
		Session session = HibernateManager.getSession();

		List<PlayerModel> list = session
				.createQuery(
						"From PlayerModel where guid in (:guids) and deletedFlag = false")
				.setParameterList("guids", guids).list();
//		HibernateManager.endSession();
		return list;
	}
	
	public static List<PlayerModel> loadAllPlayerModelsByGuid() {
		Session session = HibernateManager.getSession();

		List<PlayerModel> list = session
				.createQuery("From PlayerModel where deletedFlag = false").list();
		HibernateManager.endSession();
		return list;
	}
	
	/**
	 * 
	 * @param start
	 * @param count
	 * @return
	 */
	public static List<PlayerModel> loadAllPlayerModels(int firstResult, int count) {
		Session session = HibernateManager.getSession();

		//一定要加order，不然每次select出來的東西不一定相同
		List<PlayerModel> list = session
				.createQuery("From PlayerModel where deletedFlag = false order by db_id")
				.setFirstResult(firstResult).setMaxResults(count).list();		
		
		HibernateManager.endSession();
		return list;
	}
	
	/**
	 * 取得玩家最後上線時間
	 */
	public static Date loadPlayerLastTime(GUID playerID){
		Session session = HibernateManager.getSession();
		Query query = session.createQuery("select logoutTime from PlayerModel where guid = :playerGuid");
		query.setParameter("playerGuid", playerID.toString());
		Date logoutTime = (Date)query.uniqueResult();
		HibernateManager.endSession();
		return logoutTime;
	}
	
	/**
	 * 取得玩家最後上線時間和上線時間
	 */
	public static Object[] loadPlayerLastTimeLoginTime(GUID playerID){
		Session session = HibernateManager.getSession();
		Query query = session.createQuery("select logoutTime, loginTime from PlayerModel where guid = :playerGuid");
		query.setParameter("playerGuid", playerID.toString());
		Object[] lastTimeLoginTime = (Object[])query.uniqueResult();
		HibernateManager.endSession();
		return lastTimeLoginTime;
	}
	
	/**
	 * 通過名稱讀取邏輯上未刪除的玩家VO,先在活躍表裡查，沒查到的話再到非活躍表裡查<br>
	 * 如果非活躍表有，則移動至活躍表，並返回<br>
	 * 
	 * @param name
	 * @return
	 */
	public static Player loadPlayerByName(String name) {
		if (name == null) {
			return null;
		}
		String[] nickName = PlayerNickNameParser.dismantleNickName(name);
		Session session = HibernateManager.getSession();
		Criteria criteria = session.createCriteria(PlayerModel.class);
		criteria.add(Restrictions.eq("name", nickName[0]));
		criteria.add(Restrictions.eq("nameNumber", nickName[1]));
		PlayerModel model = (PlayerModel) criteria.uniqueResult();
		HibernateManager.endSession();
		return DataTransfer.transfer(model);
	}
	
	/**
	 * 獲取玩家所在的ZoneID
	 * 
	 * @param guid
	 * @return
	 * 
	 */
	public static String getPlayerZoneIDByGuid(GUID guid) {
		Session session = HibernateManager.getSession();
		Query query = session
				.createQuery("select zoneId from PlayerModel where guid = :guid and deletedFlag = false");
		query.setParameter("guid", GUID.toString(guid));
		String name = (String) query.uniqueResult();
		
		HibernateManager.endSession();
		return name;
		
	}

	/**
	 * 通過GUID獲得邏輯上未刪除的玩家的名稱,先在活躍表裡查，沒查到的話再到非活躍表裡查<br>
	 * 
	 * @param guid
	 * @return
	 * 
	 */
	public static String getPlayerNameByGuid(GUID guid) {
		Session session = HibernateManager.getSession();
		Query query = session
				.createQuery("select name,nameNumber from PlayerModel where guid = :guid");
		query.setParameter("guid", GUID.toString(guid));
		
		Object[] obj = (Object[]) query.uniqueResult();
		HibernateManager.endSession();
		
		String name = PlayerNickNameParser.mergeNickName(String.valueOf(obj[0]), String.valueOf(obj[1])); 
		return name;
	}

	/**
	 * 通過名稱獲得玩家的GUID，先在活躍表裡查，沒查到的話再到非活躍表裡查<br>
	 * 
	 * @param name
	 * @return
	 * 
	 */
	public static GUID getPlayerGuidByName(String name) {
		String[] nickName = PlayerNickNameParser.dismantleNickName(name);
		
		Session session = HibernateManager.getSession();
		Query query = session
				.createQuery("select guid from PlayerModel where name = :name and nameNumber = :nameNumber");
		query.setParameter("name", nickName[0]);
		query.setParameter("nameNumber", nickName[1]);
		String guid = (String) query.uniqueResult();
		if (name == null) {
			Query query2 = session
					.createQuery("select guid from InactivePlayerModel where name = :name");
			query2.setParameter("name", name);
			String guid2 = (String) query2.uniqueResult();
			HibernateManager.endSession();
			return GUID.parseUUID(guid2);
		} else {
			HibernateManager.endSession();
			return GUID.parseUUID(guid);
		}
	}
	
	/**
	 * 通過名稱列表，獲得玩家的GUID列表<br>
	 * @param name
	 * @return
	 */
	public static ArrayList<GUID> getPlayerGuidListByNameList(ArrayList<String> name) {
		ArrayList<String> names = new ArrayList<String>();
		ArrayList<String> nameNumbers = new ArrayList<String>();
		for(String n:name){
			String[] tmpStr = PlayerNickNameParser.dismantleNickName(n);
			names.add(tmpStr[0]);
			nameNumbers.add(tmpStr[1]);
		}
		Session session = HibernateManager.getSession();
		Query query = session
				.createQuery("select guid,name,nameNumber from PlayerModel where name in (:name) and nameNumber in (:nameNumber)");
		query.setParameterList("name", names);
		query.setParameterList("nameNumber", nameNumbers);
		ArrayList<Object[]> guidStrList = (ArrayList<Object[]>) query.list();
		HibernateManager.endSession();
		
		ArrayList<GUID> guidList = new ArrayList<GUID>(); 
		
		for(Object[] obj : guidStrList){
			String nickName = PlayerNickNameParser.mergeNickName( String.valueOf(obj[1]) , String.valueOf(obj[2]) );
			if(name.contains(nickName)){
				guidList.add(GUID.parseUUID(String.valueOf(obj[0])));
			}	
		}
		
		return guidList;
	}
	
	/**
	 * 通過玩家的GUID獲得玩家的頭像<br>
	 * 
	 * @param name
	 * @return
	 * 
	 */
	public static PlayerModel getGuidAndFacePhotoByName(String name) {
		String[] nameValues = PlayerNickNameParser.dismantleNickName(name);
		String nickname = nameValues[0];
		String nameNumber = nameValues[1];
		 
		Session session = HibernateManager.getSession();
		Query query = session
				.createQuery("select new PlayerModel(name,guid,facephoto) from PlayerModel where name = :name and nameNumber = :nameNumber");
		query.setParameter("name", nickname);
		query.setParameter("nameNumber", nameNumber);
		PlayerModel model = (PlayerModel) query.uniqueResult();
		HibernateManager.endSession();
		model.setName(name);
		
		return model;
	}
	
	/**
	 * 取得玩家頭像清單 by GUID <br>
	 * for開機初始化時把群組內的玩家頭像資料一併載入
	 * guid , FacePhoto
	 */
	public static HashMap<String,String> getPlayerFacePhotoList(List<String> guidList){
		if(guidList == null || guidList.size() == 0){
			return new HashMap<String,String>();
		}
		Session session = HibernateManager.getSession();
		Query query = session.createQuery("select guid,facephoto from PlayerModel where guid in (:guids)");
		query.setParameterList("guids", guidList);
		List<Object[]> list = (List<Object[]>)query.list();
		HibernateManager.endSession();
		HashMap<String,String> map = new HashMap<String,String>();
		for(Object[] obs:list){
			map.put(String.valueOf(obs[0]) , String.valueOf(obs[1]) );
		}
		return map;
	}
	
	/**
	 * 取得玩家頭像清單 by name <br>
	 * name , FacePhoto
	 */
	public static HashMap<String,String> getPlayerFacePhotoListByName(List<String> nameList){
		ArrayList<String> names = new ArrayList<String>();
		ArrayList<String> nameNumbers = new ArrayList<String>();
		for(String str:nameList){
			String[] tmpStr = PlayerNickNameParser.dismantleNickName(str);
			names.add(tmpStr[0]);
			nameNumbers.add(tmpStr[1]);
		}
		
		if(nameList == null || nameList.size() == 0){
			return new HashMap<String,String>();
		}
		Session session = HibernateManager.getSession();
		Query query = session.createQuery("select name,nameNumber,facephoto from PlayerModel where name in (:names) and nameNumber in (:nameNumbers)");
		query.setParameterList("names", names);
		query.setParameterList("nameNumbers", nameNumbers);
		List<Object[]> list = (List<Object[]>)query.list();
		HibernateManager.endSession();
		HashMap<String,String> map = new HashMap<String,String>();
		for(Object[] obs:list){
			String nickname = PlayerNickNameParser.mergeNickName(String.valueOf(obs[0]), String.valueOf(obs[1]));
			if(nameList.contains(nickname)){
				map.put(nickname , String.valueOf(obs[2]));
			}
		}
		return map;
	}
	
	/**
	 * 通過玩家的GUID獲得玩家的錯誤次數和時間<br>
	 * 
	 * @param name
	 * @return
	 * 
	 */
	public static PlayerModel getErrorTimesAndDisableTimeByGuid(GUID playerID) {
		Session session = HibernateManager.getSession();
		Query query = session
				.createQuery("select new PlayerModel(guid, errorTimes, disableTime, errorTimesGift, disableTimeGift) from PlayerModel where guid = :guid");
		query.setParameter("guid", playerID.toString());
		PlayerModel model = (PlayerModel) query.uniqueResult();
		HibernateManager.endSession();
		return model;
	}

	/**
	 * 不檢查刪除標記查活躍表讀取玩家模型
	 * 
	 * @param guid
	 * @return
	 * 
	 */
	public static PlayerModel loadUncheckedPlayerModelByGuid(GUID guid) {
		return loadUncheckedPlayerModelByGuid( GUID.toString(guid));
	}
	public static PlayerModel loadUncheckedPlayerModelByGuid(String guid) {

		if (guid == null) {
			return null;
		}
		Session session = HibernateManager.getSession();
		Criteria criteria = session.createCriteria(PlayerModel.class);
		criteria.add(Restrictions.eq("guid", guid.toString()));
		PlayerModel model = (PlayerModel) criteria.uniqueResult();
		HibernateManager.endSession();
		return model;
	}

	/**
	 * 不檢查刪除標記查活躍表讀取多個玩家模型
	 * 
	 * @param guids
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static List<PlayerModel> loadUncheckedPlayerModelsByGuid(
			List<String> guids) {
		if (guids == null || guids.size()==0) {
			return null;
		}
		Session session = HibernateManager.getSession();

		List<PlayerModel> list = session.createQuery(
				"From PlayerModel where guid in (:guids)").setParameterList(
				"guids", guids).list();
		HibernateManager.endSession();
		return list;
	}

	/**
	 * 不檢查刪除標記通過GUID獲得玩家的名稱,先在活躍表裡查，沒查到的話再到非活躍表裡查<br>
	 * 
	 * @param guid
	 * @return
	 * 
	 */
	public static String getUncheckedPlayerNameByGuid(String guid) {
		Session session = HibernateManager.getSession();
		Query query = session
				.createQuery("select name,nameNumber from PlayerModel where guid = :guid");
		query.setParameter("guid", guid);
		Object[] obj = (Object[])query.uniqueResult();
		HibernateManager.endSession();
		
		String name = PlayerNickNameParser.mergeNickName(String.valueOf(obj[0]), String.valueOf(obj[1]));
		return name;
	}

	/**
	 * 不檢查刪除標記通過名稱獲得玩家的GUID,先在活躍表裡查，沒查到的話再到非活躍表裡查<br>
	 * 
	 * @param name
	 * @return
	 * 
	 */
	public static String getUncheckedPlayerGuidByName(String name) {
		String[] nickName = PlayerNickNameParser.dismantleNickName(name);
		
		Session session = HibernateManager.getSession();
		Query query = session
				.createQuery("select guid from PlayerModel where name = :name and nameNumber = :nameNumber");
		query.setParameter("name", nickName[0]);
		query.setParameter("nameNumber", nickName[1]);
		String guid = (String) query.uniqueResult();
		HibernateManager.endSession();
		return guid;
	}

	/**
	 * 從數據庫中活躍表刪除玩家PO(不檢查刪除標記)
	 * 
	 * @param inactiveModel
	 * @return
	 */
	public static boolean delete(PlayerModel playerModel) {
		PlayerModel model = loadUncheckedPlayerModelByGuid(playerModel
				.getGuid());
		if (model != null) {
			Session session = HibernateManager.getSession();
			HibernateManager.txBegin();
			session.delete(model);
			return HibernateManager.txCommitFailRollbackClearSession();
		}
		return false;
	}

	/**
	 * 通過GUID從數據庫活躍表中刪除玩家(不檢查刪除標記)
	 * 
	 * @param guid
	 * @return
	 * 
	 */
	public static boolean deletePlayerByGuid(GUID guid) {
		PlayerModel player = loadUncheckedPlayerModelByGuid(guid);
		if (player != null) {
			Session session = HibernateManager.getSession();
			HibernateManager.txBegin();
			session.delete(player);
			return HibernateManager.txCommitFailRollbackClearSession();
		}
		return false;
	}
	
	/**
	 * 保存或更新實體對像
	 * @param entity 待處理的實體對像
	 */
	public static void saveOrUpdate(PlayerModel entity){
		Session session=HibernateManager.getSession();
		boolean isNew = entity.getDb_id() == null;
		session.saveOrUpdate(entity);
	}

	/**
	 * 扣錢，檢查相關條件是否正確
	 * <p>
	 * 可以同時異動好幾個幣別，只要有一個幣別發生錯誤就不會繼續往下處理。
	 * 
	 * @param param
	 *            相關條件參數，
	 *            執行結果會放在 {@link MoneyChangeParam#getResult()}，如果取得的值 =
	 *            {@link ErrorCode#CHANGE_MONEY_COIN_ERROR} 就要看各個幣別裡面的 error
	 *            code {@link ChangeCoin#getResult()} 才能知道正確的原因。
	 * @return success: player model<br>
	 *         fail: null
	 */
	public static void changeMoneyVerify(Player player, MoneyChangeParam param) {
		// 沒有給執行參數
		if (param == null) {
			return;
		}

		// 沒有交易id
		if (param.getOrderId() == null) {
			param.setResult(ErrorCode.NO_ORDER_ID);
			return;
		}
		
		// 取得玩家資料
		if (player == null) {
			param.setResult(ErrorCode.NO_PLAYER_DATA);
			return;
		}

		// 領取紅包不需要驗證目前身上的錢
		if (param.getChangeWay().isAddHonboToBank()) {
			param.setResult(ErrorCode.SUCCESS);
			return;
		}

		// 幣別清單是否有資料
		boolean haveCoin = true;
		ChangeCoinLister coinLister = param.getChangeCoinLister();
		//如果沒有使用道具 ， 又沒有錢 
		if (coinLister == null || coinLister.getSize() == 0) {
			haveCoin = false;
		}

		// 掉落道具是否有資料
		boolean haveDropItem = false;
		boolean isUseItemInGame = false;
		GameDropItemParam dropItemParam = param.getDropItemParam();
		if(dropItemParam != null) {
			if(dropItemParam.isHaveReward()) {
				haveDropItem = true;
			}
			
			if(dropItemParam.isUseItemInGame()) {
				isUseItemInGame = true;
			}
		}

		// 如果是得分，幣別和道具有指定一項就可以
		if (param.getChangeWay().isDailyScore()) {
			if (!haveCoin && !haveDropItem) {
				param.setResult(ErrorCode.CHANGE_MONEY_NO_COIN_LISTER);
				return;
			}
		} else {
			// 其他沒有指定幣別就不行
			if (!haveCoin) {
				param.setResult(ErrorCode.CHANGE_MONEY_NO_COIN_LISTER);
				return;
			}
		}

		// 掃一遍所有的幣別
		Iterator<Map.Entry<CoinType, ChangeCoin>> it = coinLister.getListIterator();
		ChangeCoin coinInfo = coinLister.getNextCoin(it);
		while (coinInfo != null) {
			// 角色身上的金額
			BigDecimal currentValue = coinInfo.getCurrentValue();

			// DB 中的金額
			BigDecimal currentValueDB = player.getCoin(coinInfo.getCoinType());

			// 順便做幣別檢查
			if (Helper.isEqual(currentValueDB, new BigDecimal(CoinType.UNKNOW_TYPE.getId()))) {
				coinInfo.setResult(ErrorCode.CHANGE_MONEY_INVALID_COIN_TYPE);
				param.setResult(ErrorCode.CHANGE_MONEY_COIN_ERROR);
				break;
			}

			// 角色身上的金錢和 db 上的不一樣
			if (!Helper.isEqual(currentValue, currentValueDB)) {
				coinInfo.setResult(ErrorCode.CHANGE_MONEY_VALUE_DIFFERENT_WITH_DB);
				param.setResult(ErrorCode.CHANGE_MONEY_COIN_ERROR);
				break;
			}

			// 變更的數值不能是 0
			if (Helper.isZero(coinInfo.getChangeValue())) {
				coinInfo.setResult(ErrorCode.CHANGE_MONEY_CHANGE_VALUE_CANT_BE_ZERO);
				param.setResult(ErrorCode.CHANGE_MONEY_COIN_ERROR);
				break;
			}

			// 變更的數值不能是負的
			if (Helper.isMinus(coinInfo.getChangeValue())) {
				coinInfo.setResult(ErrorCode.CHANGE_MONEY_CHANGE_VALUE_CANT_BE_NEGATIVE);
				param.setResult(ErrorCode.CHANGE_MONEY_COIN_ERROR);
				break;
			}

			// 變更後超過最大值檢查
			if (param.getChangeWay().isAdd()) {
				// 允許增加的最大量(還是限制在LONG的範圍內)
				BigDecimal diff = Helper.sub(new BigDecimal(Long.MAX_VALUE), currentValue);
				if (Helper.isGreater(coinInfo.getChangeValue(), diff)) {
					coinInfo.setResult(ErrorCode.CHANGE_MONEY_RESULT_MORE_THEN_MAX_VALUE);
					param.setResult(ErrorCode.CHANGE_MONEY_COIN_ERROR);
					break;
				}
			}

			// 變更後低於 0
			if (param.getChangeWay().isSubtract()) {
				// 不夠扣
				if (Helper.isLess(currentValue, coinInfo.getChangeValue())) {
					coinInfo.setResult(ErrorCode.CHANGE_MONEY_CURRENT_VALUE_NOT_ENOUGH);
					param.setResult(ErrorCode.CHANGE_MONEY_COIN_ERROR);
					break;
				}
			}

			// 處理下一個
			coinInfo.setResult(ErrorCode.SUCCESS);
			coinInfo = coinLister.getNextCoin(it);
		}

		if (param.getResult() == ErrorCode.CHANGE_MONEY_COIN_ERROR) {
			return;
		}

		// 驗證掉落道具 or 使用道具
		if ((param.getChangeWay().isDailyScore() && haveDropItem) || (param.getChangeWay().isDailyBet() && isUseItemInGame)) {
			// 掉落水晶
			if (!dropItemParam.isUseItemInGame() && dropItemParam.getDropCrystal() < 0) {
				param.setResult(ErrorCode.CHANGE_MONEY_INVALID_GAME_DROP_CRYSTAL_COUNT);
				return;
			}

			//掉落或是使用道具
			if (dropItemParam.isHaveItem()) {
				ItemChangeParam itemChangeParam = new ItemChangeParam();
				ChangeItemLister changeItemLister = new ChangeItemLister();
				
				ItemChangeWay changeWay = null;
				if (param.getChangeWay().isDailyScore()) {
					changeWay = ItemChangeWay.ADD_IN_GAME;
				} else if (param.getChangeWay().isDailyBet()) {
					changeWay = ItemChangeWay.SUB_USE_ITEM;
				}
				
				itemChangeParam.setOrderId(param.getOrderId());
				itemChangeParam.setChangeWay(changeWay);
				itemChangeParam.setChangeItemLister(changeItemLister);
				itemChangeParam.setGameHallId(param.getGameId());
				itemChangeParam.setGameHallName(param.getGameHallId());
				itemChangeParam.setAccountId(param.getAccountId());
				itemChangeParam.setPlayerId(param.getPlayerId());
				itemChangeParam.setPlayerName(param.getPlayerName());
				itemChangeParam.setUserType(param.getUserType());
				itemChangeParam.setTargetId(null);
				itemChangeParam.setTargetName(null);
				itemChangeParam.setSrcModuleId(null);

				// 道具 將要給的東西填入 changeItemLister
				HashMap<String, GameDropItemInfo> dropItemMap = dropItemParam.getDropItemMap();
				for (Map.Entry<String, GameDropItemInfo> entry : dropItemMap.entrySet()) {
					GameDropItemInfo info = entry.getValue();
					if (!ItemInfoManager.getInstance().isItemExist(info.getId())) {
						param.setResult(ErrorCode.CHANGE_MONEY_INVALID_GAME_DROP_ITEM_ID);
						return;
					}

					// 道具數量
					if (info.getCount() <= 0) {
						param.setResult(ErrorCode.CHANGE_MONEY_INVALID_GAME_DROP_ITEM_COUNT);
						return;
					}
					changeItemLister.addItem(info.getItemSn(), info.getId(), info.getCount(), 0);
				}

				// changeItemLister 填入目前數量
				Iterator<Map.Entry<String, ChangeItem>> changeItemIt = changeItemLister.getListIterator();
				ChangeItem changeItem = changeItemLister.getNextItem(changeItemIt);
				while (changeItem != null) {
					int itemCnt = dropItemParam.getDropItemMap().get(changeItem.getItemId()).getCurrentCount();
					changeItem.setCurrentCount(itemCnt);
					changeItem.setItemSn(changeItem.getItemSn());
					changeItem = changeItemLister.getNextItem(changeItemIt);
				}
				
				// 驗證
				ItemChangeProcessData itemChangeProcessData = ItemDAO.changeItemVerify(itemChangeParam);
				if(itemChangeParam.getResult() != ErrorCode.SUCCESS) {
					param.setResult(itemChangeParam.getResult());
					return;
				}
				
				param.setItemChangeProcessData(itemChangeProcessData);
				param.setItemChangeParam(itemChangeParam);
			}
		}

		param.setResult(ErrorCode.SUCCESS);
		return;
	}
	
	/**
	 * 扣錢，將執行後的結果寫回 db
	 * 
	 * @param param
	 *            相關條件參數 {@link MoneyChangeParam}<br>
	 *            執行結果會放在 {@link MoneyChangeParam#getResult()}
	 * @param model
	 *            將變更後的數值寫回 db 用的，model 用
	 *            {@link #changeMoneyVerify(MoneyChangeParam)} 取得。
	 * @param updateAttrList
	 *            記錄屬性更新的列表, 有指定的話異動過得屬性惠存一份在列表中, 最後再一起 update 到 db,
	 *            沒有指定的話會在結束前直接存進 db
	 */
	public static void changeMoneyApply(MoneyChangeParam param, Player player) {
		// 沒有給執行參數
		if (param == null) {
			return;
		}

		// 沒有玩家資料
		if (player == null) {
			param.setResult(ErrorCode.INVALID_PARAM);
			return;
		}

		// 上一個動作沒有成功就不給做
		if (param.getResult() != ErrorCode.SUCCESS) {
			return;
		}

		// 處理所有的幣別
		ChangeCoinLister coinLister = param.getChangeCoinLister();
		Iterator<Map.Entry<CoinType, ChangeCoin>> it = coinLister.getListIterator();
		ChangeCoin coinInfo = coinLister.getNextCoin(it);
		while (coinInfo != null) {
			// 角色身上的金額
			BigDecimal currentValue = coinInfo.getCurrentValue();

			// 變更
			if (param.getChangeWay().isAdd()) { // 增加
				currentValue = Helper.add(currentValue, coinInfo.getChangeValue());
			} else { // 扣
				currentValue = Helper.sub(currentValue, coinInfo.getChangeValue());
			}

			// 寫入 model
			player.setCoin(coinInfo.getCoinType(), currentValue);
			
			// 放入回傳值中
			coinInfo.setNewValue(currentValue);

			// 取得下一個
			coinInfo = coinLister.getNextCoin(it);
		}

		// 成功
		param.setResult(ErrorCode.SUCCESS);
	}

	/**
	 *  
	 */
	public static boolean checkIsRobortById(GUID id) {
		Session session = HibernateManager.getSession();
		Query query = session.createQuery("select userType from PlayerModel where guid = :id");
		query.setParameter("id", id.toString());
		int result = (Integer) query.uniqueResult();
		HibernateManager.endSession();

		if (result == 1) {
			return true;
		}

		return false;
	}
	
	/**
	 * 建立立即玩角色
	 * 
	 * @param accountId
	 *            account id
	 * @param nameNum
	 *            名稱編號
	 * @return
	 */
	public static Player createTrialPlayer(GUID accountId, int trialAccountGroup, int nameNum) {
		if (accountId == null) {
			return null;
		}

		// 建一個新的 player
		GUID playerGuid = GUIDFactory.createUUID(GUIDType.TYPE_PLAYER);
		String name = AccountModule.getTrialPlayerName(trialAccountGroup);
		String nameNumber = PlayerNickNameParser.nameNumberToString(nameNum);
		boolean gender = (GameUtility.randomInt(1, 2) == 1);
		String facephoto = BasicSettingUtil.getRandomFaceIconId();

		// 建
		Player player = UnitManager.createPlayer(gender, accountId);

		player.setGuid(playerGuid);
		player.setName(name);
		player.setNameNumber(nameNumber);
		player.setFacephoto(facephoto);
		player.setTrialAccount(true);
		player.setTrialCoin(new BigDecimal(BasicSetting.getInstance().getDefaultTrialCoin()));
		player.setCreateTime(System.currentTimeMillis());

		// 存入 db
		int[] errorCode = { 0 };
		if (!savePlayer(player, errorCode)) {
			return null;
		}

		return player;
	}
	
	/**
	 * h5 預設角色
	 */
	public static Player createH5DefaultCharacter(GUID accountId, GUID playerId, String cooperatorAccountId,
			String ezName, String playerName, BigDecimal goldCoin, String vendorId, String subvendorId,
			String agentData) {
		if (accountId == null) {
			return null;
		}

		// 建一個新的 player
		String nameNumber = "0000";
		boolean gender = true;
		String facephoto = BasicSettingUtil.getRandomFaceIconId();

		// 建
		Player player = UnitManager.createPlayer(gender, accountId);

		player.setCooperatorAccountId(cooperatorAccountId);
		player.setVenderId(vendorId);
		player.setSubVenderId(subvendorId);
		player.setGuid(playerId);
		player.setName(playerName);
		player.setNameNumber(nameNumber);
		player.setFacephoto(facephoto);
		player.setCreateTime(System.currentTimeMillis());
		player.setLevel(50);
		player.setVip(1);
		player.setExp(BigDecimal.ZERO);
		player.setgoldCoin(goldCoin);
		player.setAgentData(agentData);

		// 清掉更新數值列表，才不會跟著被存到 redis 中
		player.clearUpdatedAttrList();

		// 存入 db
		if (!savePlayerToRedis(player)) {
			if (log.isErrorEnabled()) {
				log.error("failed to save player to db - playerName: " + playerName + ", accountId: " + accountId
						+ ", cooperatorAccountId: " + cooperatorAccountId + ", vendorId: " + vendorId
						+ ", subvenderId: " + subvendorId);
			}
			return null;
		}

		return player;
	}
	
	public static final String PLAYER_DATA_REDIS_KEY = "PLAYER_DATA";

	public static String getPlayerDataRedisName(GUID playerId) {
		String name = ConsistentGroupName.lookUp(ModuleName.GROUP_PLAYER_DATA_REDIS, playerId);
		return name;
	}

	/**
	 * load player data from redis
	 * 
	 * @param playerId
	 */
	public static Player loadPlayerFromRedis(GUID playerId) {
		if (playerId == null) {
			if (log.isErrorEnabled()) {
				log.error(GameUtility.getCurMethodName() + "() playerId is null");
			}

			return null;
		}

		// 取得 redis resource
		String redisName = getPlayerDataRedisName(playerId);
		Jedis jedis = JedisManager.getPlayerDataResource(redisName);

		if (jedis == null) {
			if (log.isErrorEnabled()) {
				log.error(GameUtility.getCurMethodName() + "() get redis " + redisName + " fail, jedis = null.");
			}

			return null;
		}

		// load
		String key = PLAYER_DATA_REDIS_KEY + ":" + playerId;
		String playerRedisJson = jedis.get(key);

		// return redis resource
		JedisManager.returnPlayerDataResource(redisName, jedis);

		if (!GameUtility.isHaveString(playerRedisJson)) {
			if (log.isErrorEnabled()) {
				log.error(GameUtility.getCurMethodName() + "() load player " + playerId + " from redis "
						+ redisName + " fail.");
			}

			return null;
		}

		// 從 json 還原資料
		PlayerRedis playerRedis = new Gson().fromJson(playerRedisJson, PlayerRedis.class);

		if (playerRedis == null) {
			if (log.isErrorEnabled()) {
				log.error(GameUtility.getCurMethodName() + "() convert player " + playerId
						+ " data from json fail.");
			}// if

			return null;
		}// if

		if (log.isInfoEnabled()) {
			log.info("player loadPlayerFromRedis: " + playerId);
		}

		return playerRedis.toPlayer();
	}

	/**
	 * 將 player 存到 redis
	 * 
	 * @param player
	 */
	public static boolean savePlayerToRedis(Player player) {
		if (player == null) {
			return false;
		}

		PlayerRedis playerRedis = new PlayerRedis(player);
		String playerRedisJson = new Gson().toJson(playerRedis);

		// 取得 redis resource
		String redisName = getPlayerDataRedisName(player.getGuid());
		Jedis jedis = JedisManager.getPlayerDataResource(redisName);

		if (jedis == null) {
			if (log.isErrorEnabled()) {
				log.error(GameUtility.getCurMethodName() + "() fail, playerid=" + player.getGuid()
						+ " jedis = null.");
			}

			return false;
		}

		// save
		String key = PLAYER_DATA_REDIS_KEY + ":" + player.getGuid().toString();
		String res = jedis.set(key, playerRedisJson);

		// return redis resource
		JedisManager.returnPlayerDataResource(redisName, jedis);

		// fail
		if (res == null || !res.equals("OK")) {
			if (log.isErrorEnabled()) {
				log.error(GameUtility.getCurMethodName() + "() fail, playerId=" + player.getGuid() + " res=" + res);
			}

			return false;
		}

		if (log.isInfoEnabled()) {
			log.info("save player " + player.getGuid() + " to redis.");
		}

		return true;
	}

	/**
	 * 從 redis 將 player 刪除
	 * 
	 * @return
	 */
	public static boolean deletePlayerFromRedis(GUID playerId) {
		if (playerId == null) {
			return false;
		}

		// 取得 redis resource
		String redisName = getPlayerDataRedisName(playerId);

		Jedis jedis = JedisManager.getPlayerDataResource(redisName);
		if (jedis == null) {
			if (log.isErrorEnabled()) {
				log.error(GameUtility.getCurMethodName() + "() fail, playerid=" + playerId + " jedis = null.");
			}
			return false;
		}

		// delete
		String key = PLAYER_DATA_REDIS_KEY + ":" + playerId;
		jedis.del(key);

		// return redis resource
		JedisManager.returnPlayerDataResource(redisName, jedis);

		if (log.isInfoEnabled()) {
			log.info("delete player " + playerId + " to redis.");
		}

		return true;
	}

	/**
	 * 設定玩家禁止送禮時間跟原因
	 * */
	public static boolean updatePlayerDisableSendGiftTimeReason(GUID guid, Integer day, long time, String reason){
		Session session = HibernateManager.txBegin();
		Query query = session.createQuery("update PlayerModel set disableSendGiftDays = :day, disableSendGiftTime = :time, disableSendGiftReason = :reason "
				+ "where guid = :guid");
		query.setParameter("day", day);
		query.setParameter("time", time);
		query.setParameter("reason", reason);
		query.setParameter("guid", guid.toString());
		query.executeUpdate();
		boolean res = HibernateManager.txCommitFailRollbackClearSession();
		return res;
	}
	
	/**
	 * 玩家登入時間
	 */
	public static void savePlayerLoginTime(Player player, GUID sessionId){

		String playerId = player.getGuid().toString();
		String session = sessionId.toString();
		
		Jedis jedis = JedisManager.getResource("redpoint");
		String key = PLAYER_LOGIN_TIME_TABLE_NAME_REDIS + ":" + playerId;
		HashMap<String, String> map = new HashMap<String, String>();
		map.put("loginTime", String.valueOf(TimeSys.currentTimeMills()));
		map.put("sessionId", session);
		jedis.hmset(key, map);
		JedisManager.returnResource("redpoint", jedis);
	}
	
	/**
	 * 取的玩家登入時間
	 */
	public static long getPlayerLoginTime(GUID playerId){
		Jedis jedis = JedisManager.getResource("redpoint");
		String key = PLAYER_LOGIN_TIME_TABLE_NAME_REDIS + ":" + playerId.toString();
		String loginTime = jedis.hget(key,"loginTime");
		JedisManager.returnResource("redpoint", jedis);
		return Long.valueOf(loginTime);
	}

	/**
	 * 將離線 spin 資料寫入 redis
	 * 
	 * @param playerId
	 * @param result
	 */
	public static void saveOfflineSpinResult(GUID playerId, OfflineSpinResultData result) {
		if (playerId == null) {
			return;
		}

		if (result == null) {
			return;
		}

		String key = PLAYER_OFFLINE_SPIN_RESULT_TABLE_NAME_REDIS;

		String field = playerId.toString();

		Gson gson = new Gson();
		String value = gson.toJson(result);

		Jedis jedis = JedisManager.getResource(ModuleName.GAMELOBBY);
		jedis.hset(key, field, value);
		JedisManager.returnResource(ModuleName.GAMELOBBY, jedis);
	}
	
	/**
	 * 從 redis 載入離線 spin 資料 (載完刪除)
	 * 
	 * @param playerId
	 * @return
	 */
	public static OfflineSpinResultData loadOfflineSpinResult(GUID playerId) {
		if (playerId == null) {
			return null;
		}

		String key = PLAYER_OFFLINE_SPIN_RESULT_TABLE_NAME_REDIS;

		String field = playerId.toString();

		Jedis jedis = JedisManager.getResource(ModuleName.GAMELOBBY);
		String value = jedis.hget(key, field);
		jedis.hdel(key, field);
		JedisManager.returnResource(ModuleName.GAMELOBBY, jedis);

		if (value == null) {
			return null;
		}

		Gson gson = new Gson();
		Type type = new TypeToken<OfflineSpinResultData>() {
		}.getType();
		OfflineSpinResultData resule = gson.fromJson(value, type);

		return resule;
	}
	
	/**
	 * 修改離線掛機停止原因&時間(若已存在停止原因或時間的不修改)
	 * @param playerGUID
	 * @param stopReason
	 */
	public static void updatePlayerOfflineSpinStopResult(GUID playerGUID, int stopReason){
		if (playerGUID == null) {
			return;
		}

		String key = PLAYER_OFFLINE_SPIN_RESULT_TABLE_NAME_REDIS;

		String field = playerGUID.toString();

		Jedis jedis = JedisManager.getResource(ModuleName.GAMELOBBY);
		String value = jedis.hget(key, field);
		
		if(value != null){
			Gson gson = new Gson();
			OfflineSpinResultData result = gson.fromJson(value, OfflineSpinResultData.class);
			//沒寫過停止原因跟時間的才允許寫入(最後一道阻擋，這樣才可以確保資訊是"第一次"修正的結果)
			if(result.getStopReason() == 0 && result.getStopTime() == 0L){
				result.setStopReason(stopReason);
				result.setStopTime(TimeSys.currentTimeSec());
				
				String valueAfterChange = gson.toJson(result);
				jedis.hset(key, field, valueAfterChange);
			}
		}
		JedisManager.returnResource(ModuleName.GAMELOBBY, jedis);
	}
	
	public static ArrayList<String> checkAccountPlayerOffineSpin(Set<GUID> playerList){
		ArrayList<String> guidStrList = new ArrayList<String>();
		for (GUID id : playerList) {
			guidStrList.add(id.toString());
		}

		Session session = HibernateManager.getSession();
		Query query = session.createQuery("Select guid From PlayerKeepSeatModel where guid in (:ID) and content like '%\"machinestat\":4%'");
		query.setParameterList("ID", guidStrList);
		guidStrList = (ArrayList<String>) query.list();
		HibernateManager.endSession();

		return guidStrList;
	}
	
	/**
	 * 是否正在離線spin
	 * @param playerId
	 * @return
	 */
	public static boolean isOfflineSpining(GUID playerId){
		if (playerId == null) {
			return false;
		}
		
		Session session =  HibernateManager.getSession();
		Query query = session.createQuery("From PlayerKeepSeatModel where guid = :ID");
		query.setParameter("ID", playerId.toString());
		PlayerKeepSeatModel playerKeepSeatModel = (PlayerKeepSeatModel) query.uniqueResult();
		HibernateManager.endSession();
		
		if(playerKeepSeatModel == null){
			return false;
		}
		
		String content = playerKeepSeatModel.getContent();

		JsonParser parser = new JsonParser();
		JsonObject rootObejct = parser.parse(content).getAsJsonObject();
		JsonElement contentElement = rootObejct.get("keep");
		
		Gson gson = new Gson();

		Type type = new TypeToken<ArrayList<GameKeepSeatData>>() {}.getType();
		ArrayList<GameKeepSeatData> keepDatas = gson.fromJson(contentElement, type);
		
		for (GameKeepSeatData gameKeepSeatData : keepDatas) {
			if(gameKeepSeatData.getMachinestat() == BasicGameModel.OFFLINE_KEEP_SPIN){
				return true;
			}
		}
		
		return false;
	}
	
	
	public static boolean UpdatePlayerBanTalkTime(String playerId, long banTime, String banTimeReason){
		Session session = HibernateManager.txBegin();
		Query query =  session.createQuery("Update PlayerModel set banTalkTime = :BANTIME ,banTalkTimeReason = :REASON where guid = :PLAYERID");
		query.setParameter("BANTIME", banTime);
		query.setParameter("PLAYERID", playerId);
		query.setParameter("REASON", banTimeReason);
		query.executeUpdate();
		return HibernateManager.txCommitFailRollbackClearSession();
	}
	
	public static byte getPlayerIdentityTypeByPlayerId(String playerId){
		Session session = HibernateManager.getSession();
		Query query =  session.createQuery("Select playerIdentityType From PlayerModel Where guid = :ID");
		query.setParameter("ID", playerId);
		byte type = (byte)query.uniqueResult();
		HibernateManager.endSession();
		return type;
	}
	
	public static HashMap<String, Integer> getPlayerGuidLevelMap(List<String> guids){
		if(guids.size() <= 0) {
			return new HashMap<String, Integer>();
		}
		
		Session session = HibernateManager.getSession();
		Query query = session.createQuery("Select new Map(guid as guid,level as level) From PlayerModel where guid in (:GUIDS)");
		query.setParameterList ("GUIDS", guids);
		ArrayList<HashMap<String, Object>> result =(ArrayList<HashMap<String, Object>>)query.list();
		HibernateManager.endSession();
		
		HashMap<String, Integer> resultMap = new HashMap<String, Integer>();
		for (HashMap<String, Object> map : result) {
			resultMap.put((String)map.get("guid"), (Integer)map.get("level"));
		}
		return resultMap;
		
	}
	
	public static boolean modifyPlayerIdentifyType(String playerId,byte type){
		Session session = HibernateManager.txBegin();
		Query query = session.createQuery("Update PlayerModel Set playerIdentityType = :TYPE Where guid = :ID");
		query.setParameter("TYPE", type);
		query.setParameter("ID", playerId);
		query.executeUpdate();
		return HibernateManager.txCommitFailRollbackClearSession();
	}
	
	/**
	 * 更新玩家控制欄位內容
	 */
	public static void updatePlayerSwitchList(String guid, String controllerString){
		Session session = HibernateManager.txBegin();
		UpdateQueryBuilder builder = new UpdateQueryBuilder();
		builder.setSession(session);
		builder.setTableName("PlayerModel");
		builder.addSetValue("playerSwitchList", controllerString);
		builder.setWhereKey("guid", guid);
		builder.executeQuery();
		HibernateManager.txCommitFailRollbackClearSession();
	}
	
	/** 讀取玩家身上水晶*/
	public static int loadPlayerCrystalCount(String guid){
		Session session = HibernateManager.getSession();
		Query query = session.createQuery("Select crystal From PlayerModel where guid =:GUID");
		query.setParameter("GUID", guid);
		Integer count = (Integer) query.uniqueResult();
		HibernateManager.endSession();
		return count;
	}
	
	/**
	 * 更新角色開過最後一個循環寶箱的等級
	 * @param playerId
	 * @param newLevelBoxLevel
	 * @return
	 */
	public static boolean updateLastLevelBoxUseLevel(GUID playerId , int newLevelBoxLevel){
		Session session = HibernateManager.txBegin(); //更新
		UpdateQueryBuilder builder = new UpdateQueryBuilder();
		builder.setSession(session);
		builder.setTableName("PlayerModel");
		builder.addSetValue("lastLevelBoxUseLevel", newLevelBoxLevel);
		builder.addWhereKey("guid", playerId.toString());
		builder.executeQuery();
		return	HibernateManager.txCommitFailRollbackClearSession();
	}
	
	/**
	 * 讀取已有角色清單
	 */
	public static Set<String> getAllMatchedPlayersNameInModule(String moduleName) {
		long tBeg = TimeSys.currentTimeMills();
		
		Session session = HibernateManager.getSession();
		Query query = session.createQuery("From PlayerModel");
		query.setFetchSize(1000);
		ScrollableResults results = query.scroll(ScrollMode.FORWARD_ONLY);

		Set<String> resultSet = new HashSet<String>();
		while (results.next()) {
			Object[] rows = results.get();
			if (rows[0] instanceof PlayerModel) {
				PlayerModel model = (PlayerModel) rows[0];
				String playerName = model.getName();
				if (moduleName.equals(ConsistentGroupName.lookUpByString(ModuleName.GROUP_PLAYER_NAME_VERIFY,
						playerName))) {
					resultSet.add(playerName);
				}
			}
		}

		results.close();
		HibernateManager.endSession();

		long tEnd = TimeSys.currentTimeMills();
		long tDuration = tEnd - tBeg;

		if (log.isInfoEnabled()) {
			log.info("load all player name, take " + tDuration + " ms");
		}

		return resultSet;
	}

	/**
	 * 更新玩家C幣
	 * @param bigDecimal
	 * @return
	 */
	public static boolean updatePlayerGoldCoin(GUID guid, BigDecimal goldAfterChange){
		Session session = HibernateManager.txBegin();

		Query query = session.createQuery("Update PlayerModel Set goldCoin = :GOLD Where guid = :GUID");
		query.setParameter("GUID", guid.toString());
		query.setParameter("GOLD", goldAfterChange);
		query.executeUpdate();

		return HibernateManager.txCommitFailRollbackClearSession();
	}
}
